'''
WLPages ('Wiki Like Pages' or 'Waylan Limberg Pages')
=========================================================================

A simple WSGI Application that serves markdown text files as html.

Author: [Waylan Limberg](http://achinghead.com/)
	
Project website: http://achinghead.com/markdown/wlpages/
Contact: waylan [at] gmail [dot] com

License: [BSD](http://www.opensource.org/licenses/bsd-license.php)

Version: 0.1 (Oct. 18, 2007)

Dependencies:
* [Python 2.3+](http://python.org)
* [Markdown 1.6+](http://www.freewisdom.org/projects/python-markdown/)
* [wsgiref](http://pypi.python.org/pypi/wsgiref)  **In Python2.5 standard Library**
* [wsgistate](http://cheeseshop.python.org/pypi/wsgistate/)  **Optional: for cacheing**
'''

import os
from markdown import Markdown
from string import Template

# --------- Globals -------------

BASE_URI = os.getcwd()
SERVER_ROOT =  BASE_URI + '/src'
TEMPLATE = BASE_URI + '/template.html'
CACHE_DIR = BASE_URI + '/cache/'
CACHE = False
DEBUG = False

# --------- The App -------------

class WLPages:
    """ 
    Wiki Like Pages -- The wsgi/markdown application
    """
    def __init__(self, environ, start_response):
        self.environ = environ
        self.start_response = start_response
        self.server_root = SERVER_ROOT
        self.template = TEMPLATE

    def __iter__(self):
        ''' Yield the response. '''
        path = self._get_path()
        if os.path.exists(path):
            if self.environ.get('QUERY_STRING', '') == 'raw':
                content = open(path).read()
                response_headers = [('Content-Type',
                                     'text/plain; charset=utf-8')]
            else:
                content = self._to_html(open(path).read())
                response_headers = [('Content-Type', 
                                     'text/html; charset=utf-8')]
            status = '200 OK'
            self.start_response(status, response_headers)
            yield content
            if DEBUG:
                 yield str(self.environ)
        else:
            status = '404 Not found!'
            response_headers = [('Content-Type', 'text/plain; charset=utf-8')]
            self.start_response(status, response_headers)
            yield "Sorry, this page doesn't exist. Check your spelling or try a differant url.\n"
            if DEBUG:
                 yield 'No such file: "%s"\n'% path

    def _to_html(self, body):
        ''' Return html after passing body through  markdown and template.'''
        md = Markdown(extensions=['meta', 'abbr', 'footnotes', 'codehilite'])
        body = md.convert(body)
        meta = md.Meta
        for k, v in meta.items():
            meta[k] = ' '.join(v)

        t = Template(open(self.template).read())
        return t.safe_substitute(meta, body=body)

    def _get_path(self):
        ''' Return full path to source file. '''
        path = self.environ.get('PATH_INFO')
        path = '%s%s'% (self.server_root, path)
        if os.path.isdir(path):
            path = '%s/%s' % (path.rstrip('/'), 'index')
        path = '%s.txt' % path.rstrip('/')
        return path

# ------------ Middleware ------------

class NormalizeUrl:
    ''' Normalize a URL to one of `/foo/` or `/foo`. '''
    
    def __init__(self, app, force_slash=True):
        '''
        @param app: The WSGI app
        @param force_slash: Boolean (defaults to True)
                            True: forces trailing slash
                            False: removes trailing slash
        '''
        self.app = app
        self.force_slash = force_slash
    
    def __call__(self, environ, start_response):
        ''' Normalize URL and return app or redirect '''
        path = environ.get('PATH_INFO')
        if self.force_slash:
            if not path.endswith('/'):
                path = '%s/' % path
        elif len(path) > 1:
            path = path.rstrip('/')
        
        if environ.get('PATH_INFO') == path:
            return self.app(environ, start_response)
        else:
            status = '301 Permanent Redirect'
            response_headers = [('Location', self._build_url(path, environ))]
            start_response(status, response_headers)
            return []
    
    def _build_url(self, path, environ):
        ''' 
        Return a complete url with the given path 
        
        @param path: The path less server info
        @param environ : The server environ
        
        This code is in the public domain and comes from: 
        http://www.python.org/dev/peps/pep-0333/#url-reconstruction
        '''
        from urllib import quote
        url = environ['wsgi.url_scheme']+'://'

        if environ.get('HTTP_HOST'):
            url += environ['HTTP_HOST']
        else:
            url += environ['SERVER_NAME']

            if environ['wsgi.url_scheme'] == 'https':
                if environ['SERVER_PORT'] != '443':
                   url += ':' + environ['SERVER_PORT']
            else:
                if environ['SERVER_PORT'] != '80':
                   url += ':' + environ['SERVER_PORT']

        #url += quote(environ.get('SCRIPT_NAME',''))
        url += quote(path)
        if environ.get('QUERY_STRING'):
            url += '?' + environ['QUERY_STRING']
        return url


# ----------Set up the app and middleware --------

wlpages = WLPages
if CACHE:
    try:
        from wsgistate.file import WsgiMemoize, FileCache
    except ImportError:
        pass
    else:
        wlpages = WsgiMemoize(wlpages, FileCache(CACHE_DIR))
wlpages = NormalizeUrl(wlpages)


# ------------- Run the server -------------

if __name__ == '__main__':
    from wsgiref.simple_server import make_server
    
    httpd = make_server('', 8000, wlpages)
    httpd.serve_forever()
